#include "Stdafx.h"

using namespace IceMaths;

AABB& AABB::Add(const AABB& aabb)
{
	Point Min; GetMin(Min);
	Point Tmp; aabb.GetMin(Tmp);
	Min.Min(Tmp);
	
	Point Max; GetMax(Max);
	aabb.GetMax(Tmp);
	Max.Max(Tmp);
	
	SetMinMax(Min, Max);
	return *this;
}

float AABB::MakeCube(AABB& cube) const
{
	Point Ext; GetExtents(Ext);
	float Max = Ext.Max();
	
	Point Cnt; GetCenter(Cnt);
	cube.SetCenterExtents(Cnt, Point(Max, Max, Max));
	return Max;
}

void AABB::MakeSphere(Sphere& sphere) const
{
	GetExtents(sphere.mCenter);
	sphere.mRadius = sphere.mCenter.Magnitude() * 1.000001f;
	GetCenter(sphere.mCenter);
}

bool AABB::IsInside(const AABB& box) const
{
	if (box.GetMin(0) > GetMin(0)) return false;
	if (box.GetMin(1) > GetMin(1)) return false;
	if (box.GetMin(2) > GetMin(2)) return false;
	if (box.GetMax(0) < GetMax(0)) return false;
	if (box.GetMax(1) < GetMax(1)) return false;
	if (box.GetMax(2) < GetMax(2)) return false;
	return true;
}

bool AABB::ComputePlanes(Plane* planes) const
{
	if (!planes) return false;
	
	Point Center, Extents;
	GetCenter(Center);
	GetExtents(Extents);
	
	planes[0].n = Point(1.0f, 0.0f, 0.0f);
	planes[1].n = Point(-1.0f, 0.0f, 0.0f);
	planes[2].n = Point(0.0f, 1.0f, 0.0f);
	planes[3].n = Point(0.0f, -1.0f, 0.0f);
	planes[4].n = Point(0.0f, 0.0f, 1.0f);
	planes[5].n = Point(0.0f, 0.0f, -1.0f);
	
	Point p0 = Point(Center.x + Extents.x, Center.y, Center.z);
	Point p1 = Point(Center.x-Extents.x, Center.y, Center.z);
	Point p2 = Point(Center.x, Center.y+Extents.y, Center.z);
	Point p3 = Point(Center.x, Center.y-Extents.y, Center.z);
	Point p4 = Point(Center.x, Center.y, Center.z + Extents.z);
	Point p5 = Point(Center.x, Center.y, Center.z - Extents.z);
	
	planes[0].d = -(planes[0].n | p0);
	planes[1].d = -(planes[1].n | p1);
	planes[2].d = -(planes[2].n | p2);
	planes[3].d = -(planes[3].n | p3);
	planes[4].d = -(planes[4].n | p4);
	planes[5].d = -(planes[5].n | p5);
	
	return true;
}

bool AABB::ComputePoints(Point* pts) const
{
	if (!pts)
		return false;
		
	Point min; GetMin(min);
	Point max; GetMax(max);
	
	pts[0] = Point(min.x, min.y, min.z);
	pts[1] = Point(max.x, min.y, min.z);
	pts[2] = Point(max.x, max.y, min.z);
	pts[3] = Point(min.x, max.y, min.z);
	pts[4] = Point(min.x, min.y, max.z);
	pts[5] = Point(max.x, min.y, max.z);
	pts[6] = Point(max.x, max.y, max.z);
	pts[7] = Point(min.x, max.y, max.z);
	
	return true;
}

const Point* AABB::GetVertexNormals() const
{
	static float VertexNormals[] = 
	{
		-INVSQRT3, -INVSQRT3, -INVSQRT3,
		INVSQRT3, -INVSQRT3, -INVSQRT3,
		INVSQRT3, INVSQRT3, -INVSQRT3,
		-INVSQRT3, INVSQRT3, -INVSQRT3,
		-INVSQRT3, -INVSQRT3, INVSQRT3,
		INVSQRT3, -INVSQRT3, INVSQRT3,
		INVSQRT3, INVSQRT3, INVSQRT3,
		-INVSQRT3, INVSQRT3, INVSQRT3
	};
	return (const Point*)VertexNormals;
}

const udword* AABB::GetEdges() const
{
	static udword Indices[] = 
	{
		0, 1,	1, 2,	2, 3,	3, 0,
		7, 6,	6, 5,	5, 4,	4, 7,
		1, 5,	6, 2,
		3, 7,	4, 0
	};
	return Indices;
}

const Point* AABB::GetEdgeNormals() const
{
	static float EdgeNormals[] = 
	{
		0, -INVSQRT2, -INVSQRT2,
		INVSQRT2, 0, -INVSQRT2,
		0, INVSQRT2, -INVSQRT2,
		-INVSQRT2, 0, -INVSQRT2,
		
		0, INVSQRT2, INVSQRT2,
		INVSQRT2, 0, INVSQRT2,
		0, -INVSQRT2, INVSQRT2,
		-INVSQRT2, 0, INVSQRT2,
		
		INVSQRT2, -INVSQRT2, 0,
		INVSQRT2, INVSQRT2, 0,
		-INVSQRT2, INVSQRT2, 0,
		-INVSQRT2, -INVSQRT2, 0
	};
	return (const Point*)EdgeNormals;
}

static const sbyte gIndexList[64][8] =
{
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    { 0, 4, 7, 3, 0,-1,-1,   4}, 
    { 1, 2, 6, 5, 1,-1,-1,   4}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    { 0, 1, 5, 4, 0,-1,-1,   4}, 
    { 0, 1, 5, 4, 7, 3, 0,   6}, 
    { 0, 1, 2, 6, 5, 4, 0,   6}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    { 2, 3, 7, 6, 2,-1,-1,   4}, 
    { 0, 4, 7, 6, 2, 3, 0,   6}, 
    { 1, 2, 3, 7, 6, 5, 1,   6}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    { 0, 3, 2, 1, 0,-1,-1,   4}, 
    { 0, 4, 7, 3, 2, 1, 0,   6}, 
    { 0, 3, 2, 6, 5, 1, 0,   6}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    { 0, 3, 2, 1, 5, 4, 0,   6}, 
    { 1, 5, 4, 7, 3, 2, 1,   6}, 
    { 0, 3, 2, 6, 5, 4, 0,   6}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    { 0, 3, 7, 6, 2, 1, 0,   6}, 
    { 0, 4, 7, 6, 2, 1, 0,   6}, 
    { 0, 3, 7, 6, 5, 1, 0,   6}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    { 4, 5, 6, 7, 4,-1,-1,   4}, 
    { 0, 4, 5, 6, 7, 3, 0,   6}, 
    { 1, 2, 6, 7, 4, 5, 1,   6}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    { 0, 1, 5, 6, 7, 4, 0,   6}, 
    { 0, 1, 5, 6, 7, 3, 0,   6}, 
    { 0, 1, 2, 6, 7, 4, 0,   6}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    { 2, 3, 7, 4, 5, 6, 2,   6}, 
    { 0, 4, 5, 6, 2, 3, 0,   6}, 
    { 1, 2, 3, 7, 4, 5, 1,   6}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}, 
    {-1,-1,-1,-1,-1,-1,-1,   0}  
};

const sbyte* AABB::ComputeOutline(const Point& local_eye, sdword& num) const
{
	Point min; GetMin(min);
	Point max; GetMax(max);
	
	int pos = ((local_eye.x < min.x) ? 1 : 0)
			+ ((local_eye.x > max.x) ? 2 : 0)
			+ ((local_eye.y < min.y) ? 4 : 0)
			+ ((local_eye.y > max.y) ? 8 : 0)
			+ ((local_eye.z < min.z) ? 16 : 0)
			+ ((local_eye.z > max.z) ? 32 : 0);
			
	num = (sdword)gIndexList[pos][7];
	
	if (!num) return null;
	
	return &gIndexList[pos][0];
}

float AABB::ComputeBoxArea(const Point& eye, const Matrix4x4& mat, float width, float height, sdword& num) const
{
	const sbyte* Outline = ComputeOutline(eye, num);
	if (!Outline)
		return -1.0f;
		
	Point vertexBox[8], dst[8];
	ComputePoints(vertexBox);
	
	for (sdword i = 0; i < num; i++)
	{
		HPoint Projected;
		vertexBox[Outline[i]].ProjectToScreen(width, height, mat, Projected);
		dst[i] = Projected;
	}
	
	float Sum = (dst[num - 1][0] - dst[0][0]) * (dst[num - 1][1] + dst[0][1]);
	
	for (int i = 0; i < num - 1; i++)
		Sum += (dst[i][0] - dst[i + 1][0]) * (dst[i][1] + dst[i + 1][1]);
		
	return Sum * 0.5f;
}
